namespace cxxcall
{

void av_data::_Reg_struct_ret ()
{
  switch (this->rsize)
    {
      case 8:
        if (!(this->flags & msvc_struct_ret) ||
            ((this->flags & (gcc_struct_ret | split_struct_ret)) !=
                           (gcc_struct_ret | split_struct_ret)))
          break;

      case 1: case 2: case 4:
        this->flags |= reg_struct_ret;
        return;
    }

  *this->curp++ = (uintptr_t)this->raddr;
}

static inline bool
av_push_aux (av_data& av, const void *ptr, int nwords)
{
  if (av.curp + nwords - 1 >= av.endp)
    return (av.set_overflow ());

  memcpy (av.curp, ptr, nwords * WSIZE);
  av.curp += nwords;
  return (true);
}

bool av_data::arg_t (intptr_t val)
{
  return (av_push_aux (*this, &val, 1));
}

bool av_data::arg (long long val)
{
  return (av_push_aux (*this, &val, 2));
}

bool av_data::arg (float val)
{
  return (av_push_aux (*this, &val, 1));
}

bool av_data::arg (double val)
{
  return (av_push_aux (*this, &val, 2));
}

#ifndef CXXCALL_LONG_DOUBLE_ALIASED

bool av_data::arg (long double val)
{
  return (av_push_aux (*this, &val, sizeof (val) / WSIZE));
}

#endif

#ifdef _MSC_VER
#  define ST_ALIGN(align)   ((align) <= 4 ? (align) : 4)
#else
#  define ST_ALIGN(align)   align
#endif

bool av_data::arg (const void *ptr, size_t size, size_t align)
{
  auto ep = (uintptr_t *)(((uintptr_t)this->curp + size +
    ST_ALIGN (align)) & -(intptr_t)ST_ALIGN (align));

  if (ep > this->endp)
    return (this->set_overflow ());

  memcpy ((char *)ep - size, ptr, size);
  this->curp = (uintptr_t *)(((uintptr_t)ep + WSIZE - 1) & ~(WSIZE - 1));
  return (true);
}

} // namespace cxxcall
